package com.java.base.exercise.threadpool;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * @Author RenPu
 * @Date 2022/3/3 9:34
 * @Version 1.0
 * @Description: 使用枚举编写保证，线程池的单例
 **/
public enum  ThreadPool {


    INSTANCE;

    private final ThreadPoolExecutor es;

    private final Logger logger = LoggerFactory.getLogger(ThreadPool.class);

    /**
     * 自定义线程名称,方便的出错的时候溯源
     */
    private  ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setUncaughtExceptionHandler((thread,throwable)->logger.error("preparation-pool {} get exception",thread,throwable))
            .setNameFormat("preparation-pool-%d").build();

    /**
     * IO密集型任务  （常出现于线程中：数据库数据交互、文件上传下载、网络数据传输等等 能够体现多核处理器的优势）
     * CPU密集型任务 (常出现于线程中：复杂算法 能体现CPU版本的优势）
     */
    final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors()+1;

    final int MAX_POOL_SIZE =  Runtime.getRuntime().availableProcessors() * 2;

    final long KEEP_ALIVE_TIME =60L;

    final TimeUnit TIME_UNIT=TimeUnit.MILLISECONDS;

    final ThreadPoolExecutor.DiscardPolicy DISCARD_POLICY = new ThreadPoolExecutor.DiscardPolicy();




    /**
     * corePoolSize    线程池核心池的大小
     * maximumPoolSize 线程池中允许的最大线程数量
     * keepAliveTime   当线程数大于核心时，此为终止前多余的空闲线程等待新任务的最长时间
     * unit            keepAliveTime 的时间单位
     * workQueue       用来储存等待执行任务的队列
     * threadFactory   创建线程的工厂类
     * handler         拒绝策略类,当线程池数量达到上线并且workQueue队列长度达到上限时就需要对到来的任务做拒绝处理
     */
    // 私有构造器
     ThreadPool() {
        es = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TIME_UNIT,
                new LinkedBlockingQueue<>(Runtime.getRuntime().availableProcessors()*1000),
                namedThreadFactory,
                DISCARD_POLICY

                /**
                 * 1、AbortPolicy：直接抛出异常
                 * 2、CallerRunsPolicy：只用调用所在的线程运行任务
                 * 3、DiscardOldestPolicy：丢弃队列里最近的一个任务，并执行当前任务。
                 * 4、DiscardPolicy：不处理，丢弃掉。
                 */
        );
    }





    /**
     *  公有方法获取单例线程池
     * @return
     */
    public ExecutorService getInstance() {
        return es;
    }


    /**
     *  设置线程池核心线程数的初始化规则，根据具体的业务场景来择用
     * @return
     */
    public void initTheadMode() {
        //es.prestartAllCoreThreads();   //预创建线程池的线程，根据核心线程数的参数进行创建
        es.prestartCoreThread();       // 根据请求的数量，逐个进行创建核心线程数
    }


    /**
     * 使用线程池创建线程并异步执行任务
     * @param r 任务
     */
    public  void newTask(Runnable r) {
        es.execute(r);
    }


    /**
     * 将执行结果返回后再执行其他的操作
     * @param r
     */
    public  void threadExecute(Runnable r){
        es.execute(r);
    }


    /**
     * 线程的结束方法
     */
    public void threadShoutDown(){
        es.shutdown();        //优雅平滑关闭，等待已经执行的线程执行完毕，再进行释放资源
        //es.shutdownNow();  //暴露释放资源，不根据当前线程的执行状态，强制中断线程的执行 interrupted形式中断线程
    }


    public static void main(String[] args) {
        ThreadPool.INSTANCE.newTask(()->{
            try {
                System.out.println("线程开始执行.........");
                TimeUnit.SECONDS.sleep(5);
                System.out.println("中国人，不骗中国人");
                System.out.println("当前线程为："+Thread.currentThread().getName()+":  {}  任务执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        ThreadPool.INSTANCE.threadShoutDown();
    }


}
